home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac Magazin/MacEasy 19
/
Mac Magazin and MacEasy Magazine CD - Issue 19.iso
/
Musik & Kunst
/
Ear Workout 2.1
/
source code
/
ear_staff_notation.cp
< prev
next >
Wrap
Text File
|
1995-12-04
|
15KB
|
508 lines
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <OSUtils.h>
#include <QuickDraw.h>
#define PI (3.141592653589793)
extern double sin(),cos(),fabs(),atan(),atan2(),sqrt(),exp(),log();
void
circle_through_three_points(double *x0,double *y0, double *r,
double *x,double *y);
void
poly_to_arc_list(double *x0,double *y0,double *r,
double *start_theta,double *delta_theta,
double *x,double *y,int n,double scale,
int flip_y);
void
draw_arc_list(int x,int y,double *x0,double *y0,double *r,
double *start_theta,double *delta_theta,
int n,double scale,double max_extra_angle);
//
// problems:
// note heads look lousy with r>1 (I've set r=1 for now)
//
int
draw_clef(
char *verb, // "draw"
char *noun, // "g_clef", "f_clef"
int x, // horiz center of clef sign
int y, // center line of staff
double h, // dist between lines of staff
void *output_info,
void *style_info)
{
// note that the y values are positive going _up_ on the screen:
#define N_G_CLEF_POINTS 21
static double g_clef_x[N_G_CLEF_POINTS] = {
1, -5, 1, 6, 0, -9,-11, -5, 2, 4, 4,
2,-1,-3,-4,-2, 1, 0, -2, -4, -6
};
static double g_clef_y[N_G_CLEF_POINTS] = {
-15,-10, -2,-10,-20,-17, -5, 5,15,22,24,
29,28,23,20, 0,-20,-25,-29, -29,-25
};
#define N_F_CLEF_POINTS 10
static double f_clef_x[N_F_CLEF_POINTS] = {
-10, -1, 3, 2,-1,-5, -7, -9,-8,-5
};
static double f_clef_y[N_F_CLEF_POINTS] = {
-12, -8, 0,18,19,18, 15, 13,11,10
};
static double g_clef_x0[N_G_CLEF_POINTS],
g_clef_y0[N_G_CLEF_POINTS],
g_clef_r[N_G_CLEF_POINTS],
g_clef_start_theta[N_G_CLEF_POINTS],
g_clef_delta_theta[N_G_CLEF_POINTS];
static double f_clef_x0[N_F_CLEF_POINTS],
f_clef_y0[N_F_CLEF_POINTS],
f_clef_r[N_F_CLEF_POINTS],
f_clef_start_theta[N_F_CLEF_POINTS],
f_clef_delta_theta[N_F_CLEF_POINTS];
static int calculated_clefs = 0;
if (!calculated_clefs) {
poly_to_arc_list(g_clef_x0,g_clef_y0,g_clef_r,
g_clef_start_theta,g_clef_delta_theta,
g_clef_x,g_clef_y,N_G_CLEF_POINTS,10.*h,1);
poly_to_arc_list(f_clef_x0,f_clef_y0,f_clef_r,
f_clef_start_theta,f_clef_delta_theta,
f_clef_x,f_clef_y,N_F_CLEF_POINTS,10.*h,1);
calculated_clefs = 1;
}
if (strcmp(verb,"draw")==0) {
if (strcmp(noun,"g_clef")==0)
draw_arc_list(x,y,g_clef_x0,g_clef_y0,g_clef_r,
g_clef_start_theta,g_clef_delta_theta,
N_G_CLEF_POINTS,.01,20.);
if (strcmp(noun,"f_clef")==0) {
Rect rr;
short xx,yy,radius;
draw_arc_list(x,y,f_clef_x0,f_clef_y0,f_clef_r,
f_clef_start_theta,f_clef_delta_theta,
N_F_CLEF_POINTS,.01,20.);
radius = .15*h;
if (radius<1) radius=1;
xx = x+.8*h;
yy = y-1.5*h;
SetRect(&rr,xx-radius,yy-radius,xx+radius,yy+radius);
FillOval(&rr,&black);
yy = y-0.5*h;
SetRect(&rr,xx-radius,yy-radius,xx+radius,yy+radius);
FillOval(&rr,&black);
}
}
return 0;
}
// Typically, max_extra_angle should be 20. This
// controls how much extra it draws on the beginning
// and end of each arc, to try to make sure they connect
// up properly. Kind of a kludge! Should improve this!
void
draw_arc_list(int x,int y,double *x0,double *y0,double *r,
double *start_theta,double *delta_theta,
int n,double scale,double max_extra_angle)
{
int i,xx1,yy1,xx2,yy2;
short theta,d,zz;
Rect rr;
for (i=0; i<=n-3; i++) {
xx1 = x0[i]+r[i]*cos(PI/180.*start_theta[i]);
yy1 = y0[i]+r[i]*sin(PI/180.*start_theta[i]);
xx2 = x0[i]+r[i]*cos(PI/180.*(start_theta[i]+delta_theta[i]));
yy2 = y0[i]+r[i]*sin(PI/180.*(start_theta[i]+delta_theta[i]));
if (i>0)
LineTo((short) xx1,(short) yy1);
// make sure we connect with previous arc
if (r[i]*scale<1000.) {
rr.left = x+scale*(x0[i]-r[i]);
rr.right = x+scale*(x0[i]+r[i]);
rr.top = y+scale*(y0[i]-r[i]);
rr.bottom = y+scale*(y0[i]+r[i]);
// make sure it doesn't leave gaps:
theta = start_theta[i];
zz = 10.*max_extra_angle/r[i];
if (zz>max_extra_angle) zz=max_extra_angle;
if (delta_theta[i]>0) {
theta -= zz;
d = delta_theta[i]+2*zz;
}
else {
theta += zz;
d = delta_theta[i]-2*zz;
}
FrameArc(&rr,theta,d);
}
else {
MoveTo((short) xx1,(short) yy1);
LineTo((short) xx2,(short) yy2);
}
MoveTo((short) xx2,(short) yy2);
// prepare to connect up with beginning of next arc
}
}
// This routine takes a list of points and figures out
// how to connect them smoothly with arcs. The arc
// connecting points i and i+1 is chosen to be an arc
// of the circle passing through points i, i+1 and i+2.
// (The final arc is based on the last 3 points.)
// It's not as smart as it could be about choosing
// the direction of the arc. To be safe, never try
// to make an arc of >=180 degrees.
// Everything is scaled up by a factor of scale.
// Should scale up quite a bit to avoid rounding errors
// in PtToAngle, but not so much that you'll overflow
// the short ints involved.
void
poly_to_arc_list(double *x0,double *y0,double *r,
double *start_theta,double *delta_theta,
double *x,double *y,int n,double scale,
int flip_y)
{
int i,j;
Rect rr;
Point p;
short theta[3];
double s;
if (flip_y) s= -1.; else s=1;
for (i=0; i<=n-3; i++) {
double xx[3],yy[3];
// For maximum precision, scale _up_ by a factor of
// 10 now, then scale down later. Invert y's at this
// point.
for (j=0; j<=2; j++) {
xx[j] = scale*x[i+j];
yy[j] = s*scale*y[i+j];
}
circle_through_three_points(x0+i,y0+i,r+i,xx,yy);
rr.left = x0[i]-r[i];
rr.right = x0[i]+r[i];
rr.top = y0[i]-r[i];
rr.bottom = y0[i]+r[i];
for (j=0; j<=2; j++) {
#if 1
theta[j] = 90.-180./PI*atan2(-yy[j]+y0[i],xx[j]-x0[i]);
#else
p.h = xx[j];
p.v = yy[j];
PtToAngle(&rr,p,theta+j);
#endif
}
start_theta[i] = theta[0];
if (i<n-3)
delta_theta[i] = theta[1]-theta[0];
else
delta_theta[i] = theta[2]-theta[0];
while (delta_theta[i]>180.)
delta_theta[i] -= 360.;
while (delta_theta[i]<-180.)
delta_theta[i] += 360.;
}
}
// Style defined by:
// h = distance between lines of staff = height of note heads
// r = longest axis of note divided by h (>=1)
// phi_deg = angle of tilt in degrees
// The only style item that must be explicitly set at the beginning
// is h; the rest have defaults.
//
// Although I currently draw note-heads as two semicircles connected
// by straight sides, there is nothing in the interface that forces
// the calling routine to know this or that would make it difficult
// to change the shape in the future.
//
// Returns 0 normally, nonzero on error.
// Error codes:
// -1 never specified h
// -2 unrecognized verb
int
draw_note_head(
char *verb, // "draw","left_tangent","right_tangent", or
// "set_r","set_h","set_phi_deg"
int x,
int y, // center of note in current coords
int if_filled, // filled, like 1/4 note, or open, like 1/2?
void *output_info, // Point * for tangents
void *style_info // pointer to double for set_r, set_h, or set_phi_deg
)
{
static double r = 1.;
static double h = -1.; // no default for h
static double phi_deg = 20.;
static int recompute_style_stuff = 1;
static double phi, // tilt angle, converted to radians
l, // length of long axis
a, // radius of circular ends
q, // 2qa = length of straight sides
sin_phi,cos_phi,a_sin_phi,a_cos_phi,q_a_cos_phi,q_a_sin_phi;
double xr,yr,xl,yl;
int verb_was_to_set_style;
verb_was_to_set_style = 0;
if (strcmp(verb,"set_r")==0) {
r = * (double *) style_info;
recompute_style_stuff = 1;
verb_was_to_set_style = 1;
}
if (strcmp(verb,"set_h")==0) {
h = * (double *) style_info;
recompute_style_stuff = 1;
verb_was_to_set_style = 1;
}
if (strcmp(verb,"set_phi_deg")==0) {
phi_deg = * (double *) style_info;
recompute_style_stuff = 1;
verb_was_to_set_style = 1;
}
if (h<0.) return -1;
if (recompute_style_stuff) {
recompute_style_stuff = 0;
phi = phi_deg*PI/180.;
sin_phi = sin(phi);
cos_phi = cos(phi);
l = r*h;
q = (r-1.) / (1.-r*fabs(sin_phi));
a = .5*h/(1.+q*fabs(sin_phi));
a_sin_phi = a*sin_phi;
a_cos_phi = a*cos_phi;
q_a_cos_phi = q*a_cos_phi;
q_a_sin_phi = q*a_sin_phi;
}
if (verb_was_to_set_style) return 0;
// center of right-hand semicircle:
xr = x + q_a_cos_phi;
yr = y - q_a_sin_phi;
// center of left-hand semicircle:
xl = x - q_a_cos_phi;
yl = y + q_a_sin_phi;
if (strcmp(verb,"right_tangent")==0) {
if (output_info != (void *) 0)
SetPt((Point *) output_info,(short) (xr+a+.5),(short) (yr+.5));
return 0;
}
if (strcmp(verb,"left_tangent")==0) {
if (output_info != (void *) 0)
SetPt((Point *) output_info,(short) (xl-a+.5),(short) (yl+.5));
return 0;
}
#if 1
if (strcmp(verb,"draw")==0) {
Rect r;
RgnHandle region1,region2,region3,whole_region;
PolyHandle p;
region1 = NewRgn();
OpenRgn();
SetRect(&r,(short) (xr-a+.5),(short) (yr-a+.5),
(short) (xr+a+.5),(short) (yr+a+.5));
FrameOval(&r);
CloseRgn(region1);
region2 = NewRgn();
OpenRgn();
p = OpenPoly();
#define ZOT 0.5
//-- flat top part:
MoveTo((short) (x-a_sin_phi+q_a_cos_phi+ZOT),
(short) (y-a_cos_phi-q_a_sin_phi+ZOT));
LineTo((short) (x-a_sin_phi-q_a_cos_phi+ZOT),
(short) (y-a_cos_phi+q_a_sin_phi+ZOT));
//-- flat bottom part:
LineTo((short) (x+a_sin_phi-q_a_cos_phi+ZOT),
(short) (y+a_cos_phi+q_a_sin_phi+ZOT));
LineTo((short) (x+a_sin_phi+q_a_cos_phi+ZOT),
(short) (y+a_cos_phi-q_a_sin_phi+ZOT));
//-- back to start:
LineTo((short) (x-a_sin_phi+q_a_cos_phi+ZOT),
(short) (y-a_cos_phi-q_a_sin_phi+ZOT));
ClosePoly();
FramePoly(p);
CloseRgn(region2);
region3 = NewRgn();
OpenRgn();
SetRect(&r,(short) (xl-a+.5),(short) (yl-a+.5),
(short) (xl+a+.5),(short) (yl+a+.5));
FrameOval(&r);
CloseRgn(region3);
whole_region = NewRgn();
UnionRgn(region1,region2,whole_region);
UnionRgn(whole_region,region3,whole_region);
//--- finally: draw it!
if (if_filled)
FillRgn(whole_region,&black);
else
FrameRgn(whole_region);
DisposeRgn(region1);
DisposeRgn(region2);
DisposeRgn(region3);
DisposeRgn(whole_region);
KillPoly(p); //deallocate memory
return 0;
}
#endif
#if 0
if (strcmp(verb,"draw")==0) {
Rect r;
SetRect(&r,(short) (xr-a+.5),(short) (yr-a+.5),
(short) (xr+a+.5),(short) (yr+a+.5));
if (if_filled)
FillArc(&r,(short) (180-phi_deg+.5),(short) -180,&black);
else
FrameArc(&r,(short) (180-phi_deg+.5),(short) -180);
if (if_filled) {
PolyHandle p;
p = OpenPoly();
MoveTo((short) (x-a_sin_phi+q_a_cos_phi+.5),
(short) (y-a_cos_phi-q_a_sin_phi+.5));
LineTo((short) (x-a_sin_phi-q_a_cos_phi+.5),
(short) (y-a_cos_phi+q_a_sin_phi+.5));
LineTo((short) (x+a_sin_phi-q_a_cos_phi+.5),
(short) (y+a_cos_phi+q_a_sin_phi+.5));
LineTo((short) (x+a_sin_phi+q_a_cos_phi+.5),
(short) (y+a_cos_phi-q_a_sin_phi+.5));
LineTo((short) (x-a_sin_phi+q_a_cos_phi+.5),
(short) (y-a_cos_phi-q_a_sin_phi+.5));
ClosePoly();
FillPoly(p,&black);
KillPoly(p);
}
else {
MoveTo((short) (x-a_sin_phi+q_a_cos_phi+.5),
(short) (y-a_cos_phi-q_a_sin_phi+.5));
LineTo((short) (x-a_sin_phi-q_a_cos_phi+.5),
(short) (y-a_cos_phi+q_a_sin_phi+.5));
MoveTo((short) (x+a_sin_phi-q_a_cos_phi+.5),
(short) (y+a_cos_phi+q_a_sin_phi+.5));
LineTo((short) (x+a_sin_phi+q_a_cos_phi+.5),
(short) (y+a_cos_phi-q_a_sin_phi+.5));
}
SetRect(&r,(short) (xl-a+.5),(short) (yl-a+.5),
(short) (xl+a+.5),(short) (yl+a+.5));
if (if_filled)
FillArc(&r,(short) (360-phi_deg+.5),(short) -180,&black);
else
FrameArc(&r,(short) (360-phi_deg+.5),(short) -180);
return 0;
}
#endif
return -2;
}
void
draw_staff(Rect *rec)
{
short t,b,l,r,y;
double h;
int i;
t = rec->top;
b = rec->bottom;
l = rec->left;
r = rec->right;
h = b-t;
h = h/4.;
for (i=0; i<=4; i++) {
y = t+h*i+.5;
MoveTo(l,y);
LineTo(r,y);
}
}
void
circle_through_three_points(double *x0,double *y0, double *r,
double *x,double *y)
{
double xm1,ym1,xm2,ym2,a1,a2,b1,b2,lx,ly,typical_scale,z;
// If either line seg is horizontal, rotate everything
// by 20 degrees and do recursion:
if (fabs(y[0]-y[1])<1e-6 || fabs(y[1]-y[2])<1e-6) {
double xx[3],yy[3];
int i;
for (i=0; i<=2; i++) {
xx[i] = x[i]*0.939693 - y[i]*0.342020;
yy[i] = x[i]*0.342020 + y[i]*0.939693;
}
circle_through_three_points(x0,y0,r,xx,yy);
*x0 = (*x0)*0.939693 + (*y0)*0.342020;
*y0 = -(*x0)*0.342020 + (*y0)*0.939693;
return;
}
// find midpoints of two line segs:
xm1 = .5*(x[0]+x[1]);
ym1 = .5*(y[0]+y[1]);
xm2 = .5*(x[1]+x[2]);
ym2 = .5*(y[1]+y[2]);
typical_scale = fabs(xm1-xm2)+fabs(ym1-ym2);
// find slopes of normals to line segs; guaranteed
// not to have any problems with overflows, because
// we already made sure nothing was exactly horizontal
a1 = -(x[1]-x[0])/(y[1]-y[0]);
a2 = -(x[2]-x[1])/(y[2]-y[1]);
// put normals in the form y=ax+b
b1 = ym1-a1*xm1;
b2 = ym2-a2*xm2;
// Solve eqns simultaneously to get intersection.
// If slopes are the same, put the intersection way
// off to one side.
if (fabs(a1-a2)==0.) {
// First try:
*x0 = .5*(xm1+xm2)+1e6*typical_scale;
*y0 = a1*(*x0)+b1;
// Second try: now make sure it about as far off as we
// wanted it:
z = fabs(*x0-.5*(xm1+xm2))+fabs(*y0-.5*(ym1+ym2));
*x0 = .5*(xm1+xm2)
+(*(x0)-.5*(xm1+xm2))*1e6*typical_scale/z;
*y0 = a1*(*x0)+b1;
}
else {
*x0 = (b1-b2)/(a2-a1);
*y0 = a1*(*x0)+b1;
}
lx = x[0] - *x0;
ly = y[0] - *y0;
*r = sqrt(lx*lx+ly*ly);
}